package ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.spring;

import ace.cmp.converter.api.AceConverter;
import ace.cmp.converter.api.ConverterService;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.DtoManager;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.impl.AbstractDtoManager;
import ace.cmp.id.generator.api.IdGenerator;
import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslRelationHelper;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.QuerydslJdbcBaseRepository;
import ace.cmp.spring.data.jdbc.querydsl.core.util.QuerydslAnnotationUtils;
import ace.cmp.spring.data.jdbc.querydsl.rsql.core.RsqlQuerydslParser;
import ace.cmp.spring.data.jdbc.querydsl.rsql.core.RsqlQuerydslVisitor;
import ace.cmp.spring.util.core.SpringUtils;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.util.Assert;

/**
 * @author caspar
 * @date 2023/9/19 17:16 use at spring context auto inject
 */
@Slf4j
public abstract class AbstractSpringDtoManager<EntityT, IdT, EntityDtoT>
    extends AbstractDtoManager<EntityT, IdT, EntityDtoT>
    implements DtoManager<EntityT, IdT, EntityDtoT>, InitializingBean {

  @Getter(AccessLevel.PROTECTED)
  private final Class<EntityT> entityClass;

  @Getter(AccessLevel.PROTECTED)
  private final Class<EntityDtoT> entityDtoClass;

  @Getter(value = AccessLevel.PROTECTED)
  @Autowired
  private ConverterService converterService;

  @Getter(value = AccessLevel.PROTECTED)
  @Autowired
  private QuerydslRelationHelper querydslRelationHelper;

  @Getter(value = AccessLevel.PROTECTED)
  @Autowired
  private RsqlQuerydslParser querydslParser;

  @Getter(value = AccessLevel.PROTECTED)
  @Autowired
  private QuerydslAnnotationUtils querydslAnnotationUtils;

  @Getter(value = AccessLevel.PROTECTED)
  @Autowired
  private RelationalMappingContext relationalMappingContext;

  @Getter(value = AccessLevel.PROTECTED)
  @Autowired
  private SpringUtils springUtils;


  @Getter(value = AccessLevel.PROTECTED)
  private QuerydslJdbcBaseRepository<EntityT, IdT> repository;

  @Getter(AccessLevel.PROTECTED)
  private Class<IdT> idClass;

  @Getter(value = AccessLevel.PROTECTED)
  private IdGenerator<IdT> idGenerator;

  @Getter(value = AccessLevel.PROTECTED)
  private RsqlQuerydslVisitor querydslVisitor;

  @Getter(value = AccessLevel.PROTECTED)
  private AceConverter<EntityT, EntityDtoT> entityToDtoConverter;

  @Getter(value = AccessLevel.PROTECTED)
  private AceConverter<EntityDtoT, EntityT> entityDtoToEntityConverter;


  public AbstractSpringDtoManager() {
    ResolvableType resolvableType = ResolvableType.forType(this.getClass());
    this.entityClass = (Class<EntityT>) resolvableType.getSuperType().getGeneric(0).getRawClass();
    this.idClass = (Class<IdT>) resolvableType.getSuperType().getGeneric(1).getRawClass();
    this.entityDtoClass = (Class<EntityDtoT>) resolvableType.getSuperType().getGeneric(2).getRawClass();
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    this.querydslVisitor = new RsqlQuerydslVisitor(this.getEntityClass());
    this.entityToDtoConverter = this.getConverterService().getOrCreate(this.getEntityClass(), this.getEntityDtoClass());
    this.entityDtoToEntityConverter = this.getConverterService().getOrCreate(this.getEntityDtoClass(), this.getEntityClass());
    this.idGenerator = springUtils.getBeanByClassWithGenerics(IdGenerator.class, this.getIdClass());
    this.repository = (QuerydslJdbcBaseRepository<EntityT, IdT>) this.springUtils
        .getBeanByClassWithGenericsObjectProvider(QuerydslJdbcBaseRepository.class, this.getEntityClass(), this.getIdClass())
        .getIfAvailable();
    Assert.notNull(this.repository, "repository is must not be null");
    Assert.notNull(this.querydslAnnotationUtils, "querydslAnnotationUtils is must not be null");

  }
}
